
import java.io.*;
import java.net.URL;
import java.text.*;
import java.util.*;
import java.util.regex.*;
import javax.xml.transform.*;
import javax.xml.transform.stream.*;
import org.twdata.pipeline.*;

/**
 *  Driver program for testing the pipeline, its performance, and its memory
 *  usage. Sets up a simple three stage pipeline reading in an XML/XHTML file,
 *  transforms it with an XSL file, then outputting the file as a String.
 */
public class PipelineDriver {

    /**
     *  The main program for the PipelineDriver class
     *
     *@param  args           The command line arguments
     *@exception  Exception  If something goes wrong
     */
    public final static void main(String[] args)
        throws Exception {
        args[args.length - 1] = args[args.length - 1].substring(0, args[args.length - 1].length() -1);

        if (args.length < 2) {
            System.out.println("Usage: PipelineDriver XML_FILE PIPELINES_FILE [NUMBER_OF_RUNS] [--show-output]");
        } else {

            // Identify the source XML/XHTML file
            File xmlFile = new File(args[0]);

            // Build the context
            Source src = new StreamSource(xmlFile);
            PipelineContext ctx = new PipelineContext();

            String rewrittenContent = "";
            try {
                // Read the XML file content as a string
                String content = readToString(args[0]);
                
                // Default to 1 run
                int runs = 1;
                
                // Optionally, the user could specify number of runs
                if (args.length > 2) {
                    runs = Integer.parseInt(args[2]);
                }
                
                // Build the pipeline
                Pipeline pipeline = buildPipeline(args[1]);
                
                // Record the start time for performance measurements
                long startTime = System.currentTimeMillis();
                
                // Run through the pipelines
                for (int cur = 0; cur < runs; cur++) {
                    rewrittenContent = runPipeline(ctx, pipeline, content);
                }
                
                // Print out performance metrics
                System.out.println("Done pipeline transformation, Time : " +
                        ((System.currentTimeMillis() - startTime) / runs) + " ms / run for "
                         + runs + " runs");
                determineMemory("gc.log");
            } catch (Exception e) {
                e.printStackTrace();
            }

            // Print out transformed result if requested
            if (args.length == 4) {
                System.out.println("Transformed Content:" + rewrittenContent);
            }
        }

    }

    /**
     *  Determine memeory used from parsing the gc log file and output results
     *  to screen
     */
    private static void determineMemory(String path)
        throws Exception {
        Runtime rt = Runtime.getRuntime();
        System.gc();
        long mem = rt.totalMemory() - rt.freeMemory();
        File logFile = new File(path);
        FileReader reader = new FileReader(logFile);
        BufferedReader log = new BufferedReader(reader);
        String line;
        long totalGC = 0;
        double totalGCTime = 0d;
        Pattern ptn = Pattern.compile("(\\d+)K->(\\d+)K.*(\\d\\.\\d+) secs");
        while ((line = log.readLine()) != null) {
            if (line.length() > 0) {
                Matcher m = ptn.matcher(line);
                if (m.find()) {
                    int start = Integer.parseInt(m.group(1));
                    int end = Integer.parseInt(m.group(2));
                    double time = Double.parseDouble(m.group(3));
                    totalGCTime += time;
                    totalGC += start - end;
                } else {
                    System.err.println("Invalid line:" + line);
                }
            }
        }
        reader.close();

        DecimalFormat df = new DecimalFormat();
        System.out.println("Memory usage, gc: " + df.format(totalGC) + "K for " + totalGCTime + " secs, total: " + df.format((totalGC * 1000) + mem));
    }

    /**
     *  Build the pipeline
     */
    private static Pipeline buildPipeline(String pipelinesPath)
        throws Exception {
            
        InputStream springConfig = PipelineDriver.class.getResourceAsStream("/pipelineConfig.xml");
        InputStream pipeConfig = new FileInputStream(pipelinesPath);
        XMLPipelineFactory factory = new XMLPipelineFactory(springConfig, pipeConfig);
        
        return factory.getPipeline("test");
    }

    /**
     *  Run the pipeline and retrieve the transformed content
     */
    private static String runPipeline(PipelineContext ctx, Pipeline pipeline, String content)
        throws Exception {
        StringWriter writer = new StringWriter();
        ctx.setResult(new StreamResult(writer));
        
        Source src = new StreamSource(new ByteArrayInputStream(content.getBytes()));
        ctx.setSource(src);

        pipeline.execute(ctx);

        return writer.toString();
    }

    /**
     *  Read the file specified at the path into a String
     */
    private static String readToString(String path)
        throws IOException {
        FileReader reader = new FileReader(path);
        char[] buf = new char[256];
        int n = 0;
        StringBuffer sb = new StringBuffer();
        while ((n = reader.read(buf)) > -1) {
            sb.append(buf, 0, n);
        }
        reader.close();
        return sb.toString();
    }
}

